using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

/// <summary>
/// lua日志定位
/// </summary>
public class LogLocation
{
    static string m_LuaRootPath;
    static string m_VSCodePath;
    static string stackTrace;

    /// <summary>
    /// 双击日志回调
    /// </summary>
    /// <param name="guid">实例ID</param>
    /// <param name="line">行数</param>
    [UnityEditor.Callbacks.OnOpenAsset(0)]
    public static bool SelectOpenLua(int guid, int line)
    {
        if (!File.Exists(m_VSCodePath))
        {
            return false;
        }
        stackTrace = GetStackTrace();

        //string allPath = Environment.GetEnvironmentVariable("Path");
        //异常日志
        if (OpenLuaLocation("LuaException：(.*?:\\d+)")) return true;
        //正常输出日志
        if (OpenLuaLocation("stack traceback:\n(.*?:\\d+)")) return true;

        //打开C#代码
        if (FindLocation(@"file:line:column (.+):0", true)) return true;
        //打开日志
        if (FindLocation(@"\(at (.+)\)", false)) return true;
        return false;
    }

    /// <summary>
    /// 在unity双击用VsCode打开脚本
    /// </summary>
    /// <param name="guid"></param>
    /// <param name="line"></param>
    [UnityEditor.Callbacks.OnOpenAsset(1)]
    public static bool DoubleSelectOpenLua(int guid, int line)
    {
        if (!File.Exists(m_VSCodePath)) { return false; }
        string path = AssetDatabase.GetAssetPath(EditorUtility.InstanceIDToObject(guid));
        string fullPath = Application.dataPath + path.Substring(0, "Assets".Length);

        if (fullPath.EndsWith(".lua"))
        {
            //命令行语句
            string args = string.Format("-r -g {0}", path.Replace("\\", "/"), fullPath);
            //还是要绝对路径启动、不然会命令行弹窗（"code"）
            CreateShellExProcess(m_VSCodePath, args, "");
            return true;
        }
        return false;
    }

    /// <summary>
    /// 打开lua文件位置
    /// </summary>
    /// <param name="pattern"></param>
    /// <returns></returns>
    private static bool OpenLuaLocation(string pattern)
    {
        var match = Regex.Match(stackTrace, pattern);
        if (!match.Success) return false;
        string pathLine = match.Groups[1].Value;
        int spliteIndex = pathLine.LastIndexOf(':'); //分隔

        string path = pathLine.Substring(0, spliteIndex); //获取日志路径
        int line = Convert.ToInt32(pathLine.Substring(spliteIndex + 1)); //获取日志行数
        string mPath = path.Replace("\t", ""); //去除/t方便路径判断

        if (!mPath.Contains(".lua"))
        {
            //没获取lua 绝对路径就拼接路径（只适合本项目）
            string currPath = m_LuaRootPath + mPath.Replace(".", "/") + ".lua";
            if (File.Exists(currPath))
                path = "\t" + currPath;
            else
                return false;
        }

        //命令行语句
        string args = string.Format("-r -g {0}:{1}", path.Replace("\\", "/"), line);
        //还是要绝对路径启动、不然会命令行弹窗（"code"）
        CreateShellExProcess(m_VSCodePath, args, "");
        //使用默认Ide 启动（但是有Ide打开lua 识别不到文件就会报错）
        InternalEditorUtility.OpenFileAtLineExternal(path.Replace("\\", "/"), line);
        return true;
    }

    /// <summary>
    /// 检测本地文件（C#脚本跳转日志）
    /// </summary>
    /// <param name="pattern"></param>
    /// <param name="bFullPath"></param>
    private static bool FindLocation(string pattern, bool bFullPath)
    {
        var match = Regex.Match(stackTrace, pattern, RegexOptions.IgnoreCase);
        if (!match.Success) return false;
        string pathLine = match.Groups[1].Value;
        int spliteIndex = pathLine.LastIndexOf(':');
        string path = pathLine.Substring(0, spliteIndex);

        int line = Convert.ToInt32(pathLine.Substring(spliteIndex + 1));
        if (!bFullPath)
        {
            path = Application.dataPath + path.Substring(0, "Assets".Length);
        }

        //命令行语句
        string args = string.Format("-r -g {0}:{1}", path.Replace("\\", "/"), line);
        //还是要绝对路径启动、不然会命令行弹窗（"code"）
        CreateShellExProcess(m_VSCodePath, args, "");
        //默认Ide 打开C# 文件
        InternalEditorUtility.OpenFileAtLineExternal(path, line);
        return true;
    }

    /// <summary>
    /// 获取堆栈跟踪
    /// </summary>
    private static string GetStackTrace()
    {
        BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
        Type consoleType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
        FieldInfo fieldInfo = consoleType.GetField("ms_ConsoleWindow", flags);
        EditorWindow consoleWindow = fieldInfo.GetValue(null) as EditorWindow;

        if (null != consoleWindow)
        {
            if (EditorWindow.focusedWindow == consoleWindow)
            {
                flags = BindingFlags.Instance | BindingFlags.NonPublic;
                FieldInfo m_ActiveText = consoleType.GetField("m_ActiveText", flags);
                return m_ActiveText.GetValue(consoleWindow).ToString();
            }
        }
        return string.Empty;
    }

    /// <summary>
    /// 创建ShellEx进程
    /// </summary>
    /// <param name="cmd">文件</param>
    /// <param name="args">命令行自变量</param>
    /// <param name="workingDir">文件夹目录</param>
    public static Process CreateShellExProcess(string cmd, string args = "", string workingDir = "", bool output = false)
    {
        var pStartInfo = new ProcessStartInfo(cmd);
        if (!string.IsNullOrWhiteSpace(args))
        {
            pStartInfo.Arguments = args;
        }

        pStartInfo.CreateNoWindow = false;
        pStartInfo.UseShellExecute = true;
        pStartInfo.RedirectStandardError = false;
        pStartInfo.RedirectStandardInput = false;
        pStartInfo.RedirectStandardOutput = output;

        if (!string.IsNullOrEmpty(workingDir))
        {
            pStartInfo.WorkingDirectory = workingDir;
        }
        return Process.Start(pStartInfo);
    }
}